"""
__author__ = 'loong'
"""
import os

from baye import resid
from baye.util import ET, warn, info, dec_raise_xml
from baye import structures
from baye import models
from baye.person import Person
from baye.city import City
from baye.tool import Tool
from baye.res import Res
from baye.condition import Condition
from baye import image


class Period:
    class meta:
        tag = '时期'
        year = '起始年'
        people = '人物清单'
        other_people = '其它人物'
        cities = '城池清单'
        tool_conditions = '道具条件'
        tool_condition_birth = '出现年'
        tool_condition_bole = '寻宝人'
        tool_condition_city = '出现地'

    def __init__(self, lib, index):
        self.lib = lib
        self.index = index
        self.year = 0
        self.people = []
        self.cities = []
        self.tool_conditions = []

    @property
    def person_name_resid(self):
        return {
            0: resid.GENERAL_NAME,
            1: resid.GENERAL_NAME2,
            2: resid.GENERAL_NAME3,
            3: resid.GENERAL_NAME4,
        }[self.index]

    def load_lib(self):
        lib = self.lib
        # people
        size = structures.sizeof(models.PERSON)
        data = lib.resources[resid.GENERAL_RESID].items[self.index]
        for i in range(structures.PERSON_MAX):
            pdata = data[size*i:]
            if not pdata:
                break
            person = Person(self, i).load_bin(pdata)
            self.people.append(person)

        self.load_skill()

        res = lib.resources[self.person_name_resid]
        for i in range(len(self.people)):
            if i == len(res.items):
                break
            self.people[i].name = structures.decode_gbk(res.items[i])

        for person in self.people:
            person.link()

        # cities
        size = structures.sizeof(models.CITY)
        data = lib.resources[resid.CITY_RESID].items[self.index]
        for i in range(len(self.lib.cities)):
            city = City(self, i).load_bin(data[size*i:])
            city.link()
            self.cities.append(city)
        self.year = structures.read_int(data[-2:], 2)

        self.load_tool_conditions()

    def load_skill(self):
        skills_data = self.lib.resources[resid.SPE_SKLID].items[self.index]
        for p in self.people:
            if self.lib.lib_version == 0:
                p.skill = skills_data[p.index]
            else:
                p.skill = structures.read_int(skills_data[p.index*2:], 2)

    def patch_skill(self):
        skill_data = []
        for p in self.people:
            skill_data.append(p.skill)
        sz = 1 if self.lib.lib_version == 0 else 2
        data = b''.join([structures.write_int(n, sz) for n in skill_data])
        self.lib.resources[resid.SPE_SKLID].items[self.index] = data

    def load_tool_conditions(self):
        data = self.lib.resources[resid.GOODS_CON].items[self.index]
        size = structures.sizeof(models.CONDITION)
        for i in range(0, len(data), size):
            con_data = data[i:]
            con = Condition()
            con.load_bin(con_data)
            self.tool_conditions.append(con)

    def patch_tool_conditions(self):
        self.unlink_tool_conditions()

        cons = []
        for con in self.tool_conditions:
            cons.append(con.bin())
        self.lib.resources[resid.GOODS_CON].items[self.index] = b''.join(cons)

    def get_old_person_index(self, name):
        return self.lib.get_old_person_index(self.index, name)

    def needing_people_queue_length(self):
        # 取最大序号
        maxid = max(*self.lib.name_map[self.index].values())
        return max(maxid+1, len(self.people))

    def pad_people(self):
        if not self.lib.name_map:
            return
        pbuffer = [None for _ in range(self.needing_people_queue_length())]
        new_pepole = []
        for p in self.people:
            ind = self.get_old_person_index(p.name)
            if ind is None:
                new_pepole.append(p)
            else:
                p.index = ind
                pbuffer[p.index] = p

        for p in new_pepole:
            for ind in range(len(pbuffer)):
                if pbuffer[ind] is None:
                    pbuffer[ind] = p
                    p.index = ind
                    info('新人物: {} 编号: {}'.format(p.name, p.index+1))
                    break
            else:
                raise Exception('pbuffer is full')

        for ind in range(len(pbuffer)):
            if pbuffer[ind] is None:
                pbuffer[ind] = Person.nil(self)
        self.people = pbuffer

    def patch(self):
        if len(self.people) > structures.PERSON_MAX:
            raise Exception('人物数量不能超过{}个'.format(structures.PERSON_MAX))

        self.pad_people()

        for p in self.people:
            if p.level:
                p.unlink()

        pnames = [structures.encode_gbk(p.name) for p in self.people]
        res = Res(self.person_name_resid)
        res.items = pnames
        self.lib.resources[self.person_name_resid] = res

        pdata = b''
        for p in self.people:
            pdata += p.bin()
        res = self.lib.resources[resid.GENERAL_RESID]
        res.items[self.index] = pdata

        condata = b''
        for p in self.people:
            condata += p.condition.bin()
        res = self.lib.resources[resid.GENERAL_CON]
        res.items[self.index] = condata

        for city in self.cities:
            city.unlink(self)

        p_queue = b''
        cur = 0
        if self.lib.lib_version == 0:
            ind_size = 1
            mask = 0x80
        else:
            ind_size = 2
            mask = 0x8000
        for city in self.cities:
            l = len(city.people)
            city.persons_queue = cur
            city.persons_count = l
            cur += l
            p_queue += b''.join([structures.write_int(p.index, ind_size) for p in city.people])
        res = self.lib.resources[resid.GENERAL_QUEUE]
        res.items[self.index] = p_queue

        t_queue = b''
        cur = 0
        for city in self.cities:
            l = len(city.tools)
            city.tools_queue = cur
            city.tools_count = l
            cur += l
            t_queue += b''.join([structures.write_int(t.index if t.hide else (t.index | mask), ind_size) for t in city.tools])
        res = self.lib.resources[resid.GOODS_QUEUE]
        res.items[self.index] = t_queue

        res = self.lib.resources[resid.CITY_RESID]
        cdata = b''
        for i in range(len(self.lib.cities)):
            city = self.get_city_by_index(i)
            cdata += city.bin()
        cdata += structures.write_int(self.year, 2)
        res.items[self.index] = cdata

        self.patch_skill()
        self.patch_tool_conditions()

    def get_city_by_index(self, index):
        for city in self.cities:
            if city.index == index:
                return city
        city = self.lib.cities[index]
        raise Exception('第{}时期缺少 {} 城池信息'.format(self.index+1, city.name))

    def create_name_map(self):
        m = {}
        for p in self.people:
            m[p.name] = p.index
        return m

    def xml(self):
        i = ET.Element(self.meta.tag)
        appearing_people = set()

        cities_node = ET.Element(self.meta.cities)
        i.append(cities_node)
        for city in self.cities:
            cities_node.append(city.xml())

            for p in city.people:
                appearing_people.add(p.index)

        plist_node = ET.Element(self.meta.other_people)
        i.append(plist_node)
        for person in self.people:
            if person.index not in appearing_people and person.level > 0:
                pnode = person.xml()
                plist_node.append(pnode)

        tool_conditions_node = ET.Element(self.meta.tool_conditions)
        i.append(tool_conditions_node)
        for ind, condition in enumerate(self.tool_conditions):
            if condition.birth > self.year and condition.city or condition.bole:
                tool = self.lib.tools[ind]
                e = ET.Element(tool.meta.tag)
                e.attrib[tool.meta.name] = tool.name
                e.attrib[self.meta.tool_condition_birth] = str(condition.birth)
                person = condition.bole and self.get_person_name(condition.bole-1) or ''
                e.attrib[self.meta.tool_condition_bole] = person
                city = self.get_city_by_index(condition.city)
                e.attrib[self.meta.tool_condition_city] = city.name
                tool_conditions_node.append(e)

        i.attrib[self.meta.year] = str(self.year)

        return i

    def save_avatars(self, path):
        data = self.lib.resources[resid.GEN_HEADPIC1 + self.index].items[0]
        images = image.ImageSet().load_data(data)
        for p in self.people:
            if p.level > 0:
                img = images.image(p.index)
                img.save(os.path.join(path, p.name + '.bmp'))

    def patch_avatars(self, path):
        images = []
        HEAD_SZ = 24 * self.lib.config.output_scale
        for p in self.people:
            if p.level > 0:
                fname = os.path.join(path, p.name + '.bmp')
                try:
                    images.append(image.Image.open(fname, HEAD_SZ, HEAD_SZ))
                except Exception:
                    alter = os.path.join(os.path.dirname(path), 'alter.bmp')
                    try:
                        images.append(image.Image.open(alter, HEAD_SZ, HEAD_SZ))
                        warn('时期{} 缺少头像 {}, 将使用缺省头像替代'.format(self.index+1, os.path.basename(fname)))
                    except Exception:
                        raise Exception('时期{} 缺少头像 {}'.format(self.index+1, os.path.basename(fname)))
            else:
                images.append(image.Image(HEAD_SZ, HEAD_SZ))

        img_set = image.ImageSet().load_images(HEAD_SZ, HEAD_SZ, images)
        self.lib.resources[resid.GEN_HEADPIC1 + self.index].items[0] = img_set.bin()

    def get_person_name(self, index):
        if 0 <= index < len(self.people):
            return self.people[index].name
        return ''

    def get_person_by_name(self, name):
        if not name:
            return -1
        for i, p in enumerate(self.people):
            if name == p.name:
                return i
        raise Exception('时期 {} 不存在人物 {}'.format(self.index+1, name))

    @classmethod
    def from_xml(cls, xml, lib, idx):
        p = Period(lib, idx)
        p.load_xml(xml)
        return p

    @dec_raise_xml
    def load_xml(self, xml):
        self.year = int(xml.attrib.get(self.meta.year))

        all_people_names = {}

        for node in xml:
            if node.tag == self.meta.cities:
                for cnode in node:
                    if cnode.tag == City.meta.tag:
                        name = cnode.attrib[City.meta.name]
                        index = self.lib.get_city_index_by_name(name)
                        city = City(self, index)
                        city.load_xml(cnode, self.lib)
                        self.cities.append(city)
                        for person in city.people:
                            person.index = len(self.people)
                            self.people.append(person)

                            if person.name in all_people_names:
                                msg = '时期{} 人物:{} 重复'.format(self.index+1, person.name)
                                warn(msg)
                                continue
                            all_people_names[person.name] = city

            elif node.tag == self.meta.other_people:
                others = []
                for pnode in node:
                    if pnode.tag == Person.meta.tag:
                        p = Person(self, 0)
                        p.load_xml(pnode)
                        if p.condition.birth + 16 <= self.year:
                            warn('时期{} 人物:{} 没有机会出场'.format(self.index+1, p.name))
                        if p.name in all_people_names:
                            warn('时期{} 人物:{} 重复'.format(self.index+1, p.name))
                            continue
                        p.index = len(self.people)
                        self.people.append(p)
                        others.append(p)
            elif node.tag == self.meta.tool_conditions:
                conditions = [Condition() for _ in self.lib.tools]
                for tnode in node:
                    if tnode.tag == Tool.meta.tag:
                        con = Condition()
                        name = tnode.attrib[Tool.meta.name]
                        con.birth = int(tnode.attrib[self.meta.tool_condition_birth])
                        city = self.lib.get_city_index_by_name(tnode.attrib[self.meta.tool_condition_city])
                        if city < 0:
                            raise ValueError('Bad condition.city')
                        else:
                            con.city = city
                        con.bole = tnode.attrib[self.meta.tool_condition_bole]
                        tool = self.lib.get_tool_by_name(name)
                        conditions[tool.index] = con
                self.tool_conditions = conditions

    def unlink_tool_conditions(self):
        for t in self.tool_conditions:
            person = self.get_person_by_name(t.bole)
            t.bole = person + 1

    def check(self):
        tool_count = 0
        for city in self.cities:
            tool_count += len(city.tools)
        if tool_count > structures.TOOLS_QUEUE_MAX:
            msg = '第{}时期 城中预埋道具太多, 埋了{}个, 不能超过 {}个'.format(self.index+1, tool_count, structures.TOOLS_QUEUE_MAX)
            raise Exception(msg)
